﻿namespace NetFx.SMIL.Definitions
{
  using System;
  using System.Text;
  using System.Collections;
  using System.Collections.Generic;

  using System.Reflection;
  using System.Reflection.Emit;
  using System.Runtime.InteropServices;

  using System.Diagnostics;
  using System.Diagnostics.CodeAnalysis;
  using System.Diagnostics.SymbolStore;
  using System.Security;
  using System.Security.Permissions;

  using NetFx.MSIL;
  using NetFx.SMIL.References;

  public class EventDef : MemberDef
  {
    #region Statics
    public static EventDef of(EventInfo eventInfo)
    {
      return new EventDef(eventInfo);
    }
    public static EventDef of(string name)
    {
      return new EventDef(name);
    }
    #endregion
    #region Fields
    protected EventAttributes attributes = EventAttributes.None;
    protected TypeRef eventHandlerType = null;
    protected bool isMultiCast = false;
    protected MethodRef addonMethod = null;
    protected MethodRef removeonMethod = null;
    protected MethodRef raiseMethod = null;
    protected List<MethodRef> otherMethods = null;
    #endregion
    #region Properties
    public override DefType DefinitionType
    {
      get { return DefType.Event; }
    }
    public virtual EventAttributes Attributes
    {
      get
      {
        return this.attributes;
      }
      set
      {
        this.attributes = value;
      }
    }
    public virtual bool IsMultiCast
    {
      get
      {
        return this.isMultiCast;
      }
      set
      {
        this.isMultiCast = value;
      }
    }
    public virtual bool IsSpecialName
    {
      get
      {
        return ((this.Attributes & EventAttributes.SpecialName) != EventAttributes.None);
      }
      set
      {
        if (value)
        {
          this.attributes |= EventAttributes.SpecialName;
        }
        else
        {
          this.attributes &= (~EventAttributes.SpecialName);
        }
      }
    }
    public virtual bool IsRTSpecialName
    {
      get
      {
        return ((this.Attributes & EventAttributes.RTSpecialName) != EventAttributes.None);
      }
      set
      {
        if (value)
        {
          this.attributes |= EventAttributes.RTSpecialName;
        }
        else
        {
          this.attributes &= (~EventAttributes.RTSpecialName);
        }
      }
    }
    public virtual TypeRef EventHandlerType
    {
      get
      {
        return this.eventHandlerType;
      }
      set
      {
        this.eventHandlerType = value;
      }
    }
    public virtual MethodRef AddonMethod
    {
      get
      {
        return this.addonMethod;
      }
      set
      {
        this.addonMethod = value;
      }
    }
    public virtual MethodRef RemoveonMethod
    {
      get
      {
        return this.removeonMethod;
      }
      set
      {
        this.removeonMethod = value;
      }
    }
    public virtual MethodRef RaiseMethod
    {
      get
      {
        return this.raiseMethod;
      }
      set
      {
        this.raiseMethod = value;
      }
    }
    public virtual bool HasAddonMethod
    {
      get
      {
        return this.AddonMethod != null;
      }
    }
    public virtual bool HasRemoveonMethod
    {
      get
      {
        return this.RemoveonMethod != null;
      }
    }
    public virtual bool HasRaiseMethod
    {
      get
      {
        return this.RaiseMethod != null;
      }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <remarks>
    /// 由于现有版本尚未实现MSIL定义中的".other"功能，所以
    /// 任何写入OtherMethod的行为都无法生成可工作的代码。
    /// </remarks>
    public virtual List<MethodRef> OtherMethods
    {
      get
      {
        return this.otherMethods == null ?
          this.otherMethods = new List<MethodRef>() :
          this.otherMethods;
      }
    }
    #endregion
    #region Constructors
    public EventDef(EventInfo eventInfo):this(String.Empty)
    {
      this.OnReflecting(eventInfo);
    }
    public EventDef(string name) : base(name) { }
    public EventDef(string name, Type eventType)
      : this(name)
    {
      this.eventHandlerType = TypeRef.of(eventType);
    }
    public EventDef(string name, Type eventType,EventAttributes attributes)
      : this(name,eventType)
    {
      this.attributes = attributes;
    }

    #endregion
    #region Methods
    #region Publics
    public virtual MethodRef AddOtherMethodRef(MethodRef method)
    {
      if (method != null)
      {
        this.OtherMethods.Add(method);
      }
      return method;
    }
    public virtual MethodRef RemoveOtherMethodRef(MethodRef method)
    {
      if (method != null)
      {
        this.OtherMethods.Remove(method);
      }
      return method;
    }
    #endregion
    #region Helpers
    protected override AnyGenerator OnCompleting(AnyGenerator generator)
    {
      EventGenerator g = generator as EventGenerator;
      if (generator !=null)
      {
        if (this.HasAddonMethod)      g.AddonMethod     = this.AddonMethod.ReflectInfo    as MethodBuilder;
        if (this.HasRemoveonMethod)   g.RemoveonMethod  = this.RemoveonMethod.ReflectInfo as MethodBuilder;
        if (this.HasRaiseMethod)      g.RaiseMethod     = this.RaiseMethod.ReflectInfo    as MethodBuilder;
      }
      return base.OnCompleting(generator);
    }
    protected override AnyGenerator OnDefining(AnyGenerator container)
    {
      if (container is TypeGenerator)
      {
        return (container as TypeGenerator).DefineEvent(this.Name, this.Attributes, this.EventHandlerType.ReflectInfo);
      }
      return base.OnDefining(container);
    }

    protected override ReflectDef OnReflecting(object source)
    {
      if (source is EventInfo)
      {
        EventInfo ei = source as EventInfo;

        this.attributes = ei.Attributes;
        this.isMultiCast = ei.IsMulticast;
        this.addonMethod = MethodRef.of(ei.GetAddMethod());
        this.removeonMethod = MethodRef.of(ei.GetRemoveMethod());
        this.raiseMethod = MethodRef.of(ei.GetRaiseMethod());

        this.ReloadMembers((MemberInfo[])ei.GetOtherMethods(), this.OtherMethods);
      }
      else if (source is EventBuilder)
      {
        //UNABLE TO RETRIEVE INFORMATION FROM EventBuilder!
      }
      return base.OnReflecting(source);
    }
    protected override void DisposeList(bool disposing)
    {
      if (disposing)
      {
        this.DisposeList(this.OtherMethods);
      }
    }
    protected override void OnBreakingReference()
    {
      this.Break(this.AddonMethod);
      this.Break(this.RemoveonMethod);
      this.Break(this.RaiseMethod);
      this.Break(this.OtherMethods);
      base.OnBreakingReference();
    }
    #endregion
    #endregion
  }
}
